﻿/*
 * This file is part of MonoSettlers.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://opensettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace MonoSettlers
{
    public unsafe class ImagePixelLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
    {
        private static byte[] buffer = new byte[1024];
        private static byte[] tmpBuffer = new byte[1024];
        private Bitmap bitmap;
        private BitmapData data;
        public Boolean IsCopy { get; private set; }
        public Int64 Checksum { get; private set; }
        public int* Pixels { get; private set; }

        public int Width { get { return bitmap.Width; } }
        public int Height { get { return bitmap.Height; } }

        public ImagePixelLock(Bitmap inSource)
            : this(inSource, new System.Drawing.Rectangle(0, 0, inSource.Width, inSource.Height), false)
        {
        }

        public ImagePixelLock(Bitmap inSource, Boolean inCreateCopy)
            : this(inSource, new System.Drawing.Rectangle(0, 0, inSource.Width, inSource.Height), inCreateCopy)
        {
        }

        public ImagePixelLock(Bitmap inSource, System.Drawing.Rectangle inLockRegion)
            : this(inSource, inLockRegion, false)
        {
        }

        public ImagePixelLock(Bitmap inSource, System.Drawing.Rectangle inLockRegion, Boolean inCreateCopy)
        {
            if (inSource.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
                throw new ArgumentException("Given bitmap has an unsupported pixel format.");

            IsCopy = inCreateCopy;

            if (inCreateCopy)
                bitmap = (Bitmap)inSource.Clone();
            else
                bitmap = inSource;

            data = bitmap.LockBits(inLockRegion, ImageLockMode.ReadWrite, inSource.PixelFormat);
            Pixels = (int*)data.Scan0.ToPointer();

            // compute checksum from pixeldata
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            int* ptr = (int*)data.Scan0.ToPointer();

            for (int i = 0, byteCount = Width * Height * 4; i < byteCount; i += buffer.Length)
            {
                int count = Math.Min(buffer.Length, byteCount - i);

                System.Runtime.InteropServices.Marshal.Copy((IntPtr)ptr, buffer, 0, count);
                md5.TransformBlock(buffer, 0, count, tmpBuffer, 0);

                ptr += count / 4;
            }

            md5.TransformFinalBlock(new byte[0], 0, 0);

            byte[] checksum = md5.Hash;

            for (int i = 0; i < 8; i++)
            {
                Checksum |= (((Int64)checksum[i]) << (i * 8));
            }
        }

        public override int GetHashCode()
        {
            return unchecked((Int32)Checksum);
        }

        ~ImagePixelLock()
        {
            Dispose();
        }

        public void Dispose()
        {
            try
            {
                if ((data != null) && (bitmap != null))
                    bitmap.UnlockBits(data);

                if (IsCopy && (bitmap != null))
                    bitmap.Dispose();
            }
            catch
            {
                // bitmap might be already disposed even if we got a valid pixellock
            }

            data = null;
            bitmap = null;
            Pixels = (int*)0;
        }
    }
}
